import tkinter as tk
import tkinter.ttk
from tkinter import StringVar, filedialog
from tkinter import scrolledtext        # 导入滚动文本框的模块
import os
import sys
#from typing_extensions import 
import paramiko
import time
from time import sleep
import re
import random
import threading
import configparser
import inspect
import ctypes

upload_start = 0
upgrade_timeout = 0
upload_cancel =0
def device_upgrade(my_ip,my_user,my_password,my_upgrade_path,my_compile_ip,ftp_user,ftp_passwd, ftp_root):
    global upgrade_timeout
    try:
        #error_text.set('Login device {} {}\n'.format(my_ip, lz_dev.get()))
        progressbarOne['value'] = 0
        myssh = paramiko.SSHClient()
        myssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        myssh.connect(my_ip, 22, my_user, my_password, allow_agent=False, look_for_keys=False, timeout=30 * 60)
        myshell = myssh.invoke_shell()
        if (lz_dev.get() == 1):
            myshell.send('exit\n')
            #error_text.set("荔枝设备先退出诊断模式")
            myshell.send('en\nupdate ftp user {} {} {} {}{} version\n'.format(ftp_user, ftp_passwd,my_compile_ip,ftp_root,my_upgrade_path)) 
           
        else:
            myshell.send('en\ncopy ftp {} {} {} {}{} version\n'.format(ftp_user, ftp_passwd,my_compile_ip,ftp_root,my_upgrade_path))
        sleep(2)
        out = myshell.recv(65535).decode()
        #print(out)
        upgrade_timeout = 0
        while True:
            out = myshell.recv(65535).decode()
            if 'Update images success' in out:
                #error_text.set('Device {} update images success\n'.format(my_ip))
                progressbarOne['value'] = 100
                status_text.set('安装完成')
                break
            elif "Download is failed." in out:
                error_text.set(out)              
                tk.messagebox.showwarning('警告','升级版本失败,检查FTP服务器和版本文件!')
                return False
            elif "Failed to read image head info." in out:              
                tk.messagebox.showwarning('警告','升级版本失败,检查FTP服务器和版本文件!')
                error_text.set(out)
                return False
            elif '%[' in out:
                status_text.set('开始下载')
                index = int(out[:out.index('%[')][-3:])
                progressbarOne['value'] = index
                if index == 100:
                    status_text.set('下载完成')
                else:
                    status_text.set('下载中{}%'.format(index))
                root.update()
            elif 'Install system.' in out:
                  progressbarOne['value'] = 100
                  status_text.set('安装中...')
            #print(out)
            sleep(1)
            upgrade_timeout = upgrade_timeout + 1
            if upgrade_timeout > 300:              
                tk.messagebox.showwarning('警告','升级版本失败,检查FTP服务器和版本文件!')
                return False                
    except:  
        if upload_cancel == 0:
            tk.messagebox.showwarning('警告','升级版本失败,检查设备IP和用户名密码!')
        return False
    finally:
        pass 

    return True


def device_reboot(my_ip,my_user,my_password,reboot,save_config):
    global upgrade_timeout
    try:
        myssh = paramiko.SSHClient()
        myssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        myssh.connect(my_ip, 22, my_user, my_password, allow_agent=False, look_for_keys=False, timeout=30 * 60)
        myshell = myssh.invoke_shell()
        if (lz_dev.get() == 1):
            myshell.send('exit\n')
        if save_config == 1:
            if reboot == 1:
                myshell.send('en\nreboot\ny\ny\n')
            else:
                myshell.send('en\nreboot\ny\nn\n')
        else: 
            if  reboot == 1:         
                myshell.send('en\nreboot\nn\ny\n')
            else:
                myshell.send('en\nreboot\nn\nn\n')
        sleep(1)
        upgrade_timeout = 0
        while True:
            out = myshell.recv(65535).decode()
            #print(out)
            if 'The system will be rebooted! Please enter "y/n"  to confirm:' in out:
                sleep(1)
                error_text.set("设备 {} 升级成功,正在重启...\n".format(my_ip));
                status_text.set('设备重启')
                break
            elif 'Save current configuration? Please enter "y/n"  to confirm:' in out:
                status_text.set('配置保存...')
            elif 'Save configuration ok !' in out:
                status_text.set('配置保存成功')
            sleep(1)
            upgrade_timeout = upgrade_timeout + 1
            if upgrade_timeout > 200:
                error_text.set("设备 {} 保存配置超时, 时间超过 2 分钟\n".format(my_ip));
                tk.messagebox.showwarning('警告','保存配置超时')
                return False                
    except:
        return False
    finally:
        myshell.close()      
        tk.messagebox.showinfo('提示','{}升级版本成功!'.format(my_ip))

def upload_file():
    global upload_start
    upload_start = 1
    error_text.set("")
    status_text.set("")
    #写入配置文件   
    config = configparser.ConfigParser()
           
    config.read(config_filename)

    if not config.has_section("UPDATE"):
        config.add_section("UPDATE")
    config.set("UPDATE", "dev_ip", dev_ip.get())
    config.set("UPDATE", "account", account.get())
    config.set("UPDATE", "password", password.get())
    config.set("UPDATE", "r_value", str(r_value.get()))
    config.set("UPDATE", "save_config", str(save_config.get()))
    config.set("UPDATE", "upload", str(upload.get()))
    config.set("UPDATE", "ftp_ip", ftp_ip.get())
    config.set("UPDATE", "ftp_account", ftp_account.get())
    config.set("UPDATE", "ftp_password", ftp_password.get())
    config.set("UPDATE", "lz_dev", str(lz_dev.get()))
    config.set("UPDATE", "ftp_root", str(ftp_root.get()))
    config.write(open(config_filename, 'w'))

    if upload.get()  == 1:        
        error_text.set("升级设备{}...".format(dev_ip.get()))
        ret = device_upgrade(dev_ip.get(), account.get(), password.get(), file_name.get(), ftp_ip.get(), ftp_account.get(), ftp_password.get(), ftp_root.get())
    if ret and r_value.get() == 1:        
        error_text.set("重启设备{}...".format(dev_ip.get()))
        device_reboot(dev_ip.get(), account.get(), password.get(), r_value.get(),save_config.get())
    upload_start = 0
    return

up_thread=''
def upload_thread():
    global upload_start
    global up_thread
    if upload_start:
        tk.messagebox.showinfo("正在升级...")
        return
    if file_name.get() == '':
        tk.messagebox.showinfo('版本名称为空\n')
        return
    up_thread = threading.Thread(target=upload_file,name='upload')
    up_thread.start()
    return

def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")
 
 
def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

def cancel_thread():
    global up_thread
    global upgrade_timeout
    global upload_start
    if up_thread != '':
        error_text.set("取消升级设备{}...".format(dev_ip.get()))
        upload_cancel = 1
        stop_thread(up_thread)
        upgrade_timeout = 300
        upload_start = 0

config_filename= os.path.join(os.path.dirname(sys.argv[0]), 'CONFIG.ini')

root = tk.Tk()
file = StringVar()
file_name = StringVar()
status_text = StringVar()
status_text.set('')
error_text = StringVar()
error_text.set('')
if os.path.exists(config_filename):
    config=configparser.ConfigParser() 
    config.read(config_filename) 

    #读取配置信息
    dev_ip = StringVar(value = config.get('UPDATE','dev_ip'))
    ftp_ip = StringVar(value = config.get('UPDATE','ftp_ip'))
    account = StringVar(value = config.get('UPDATE','account'))
    password = StringVar(value = config.get('UPDATE','password'))
    r_value = tk.IntVar(value = config.get('UPDATE','r_value'))
    save_config = tk.IntVar(value = config.get('UPDATE','save_config'))
    upload = tk.IntVar(value = config.get('UPDATE','upload'))
    ftp_account = StringVar(value = config.get('UPDATE','ftp_account'))
    ftp_password = StringVar(value = config.get('UPDATE','ftp_password'))
    lz_dev = tk.IntVar(value = config.get('UPDATE','lz_dev')) 
    ftp_root = StringVar(value = config.get('UPDATE','ftp_root'))
else:
    dev_ip = StringVar(value = '10.212.2.20')
    ftp_ip = StringVar(value = '192.168.193.254')
    account = StringVar(value = 'admin')
    password = StringVar(value = 'admin@123')
    ftp_account = StringVar()
    ftp_password = StringVar()
    r_value = tk.IntVar(value=1)
    save_config = tk.IntVar(value=1)
    upload = tk.IntVar(value=1)
    lz_dev = tk.IntVar(value=0)
    ftp_root = StringVar(value = 'version/')

frm = tk.Frame(width=500, height=320)
frmlog = tk.Frame(width=500, height=10)
root.title('ABT-版本上传')

frmlog.grid(row=0, column=0,padx=50,pady=8)
frm.grid(row=1, column=0,padx=80,pady=8)
n_row = 0
#frm.place(x=100, y=100, width=8000, height=800)
#print ("frame size before update: ", frm.winfo_width(), frm.winfo_height())
tk.Label(frm, text="设备地址").grid(row=n_row,column=0)
ip = tk.Entry(frm, textvariable=dev_ip,width='40').grid(row=n_row,column=1)

tk.Checkbutton(frm, text = "荔枝",  variable=lz_dev).grid(row=n_row,column=2)
n_row += 1

tk.Label(frm, text="设备账号").grid(row=n_row,column=0)
user = tk.Entry(frm, textvariable=account,width='40').grid(row=n_row,column=1)
n_row += 1

tk.Label(frm, text="设备密码").grid(row=n_row,column=0)
passwd = tk.Entry(frm, textvariable=password,width='40').grid(row=n_row,column=1)
n_row += 1

tk.Label(frm, text="FTP地址").grid(row=n_row,column=0)
passwd = tk.Entry(frm, textvariable=ftp_ip,width='40').grid(row=n_row,column=1)
n_row += 1

tk.Label(frm, text="FTP账号").grid(row=n_row,column=0)
user = tk.Entry(frm, textvariable=ftp_account,width='40').grid(row=n_row,column=1)
n_row += 1

tk.Label(frm, text="FTP密码").grid(row=n_row,column=0)
passwd = tk.Entry(frm, show="*",textvariable=ftp_password,width='40').grid(row=n_row,column=1)
n_row += 1

tk.Label(frm, text="FTP根路径").grid(row=n_row,column=0)
version = tk.Entry(frm, textvariable=ftp_root,width='40').grid(row=n_row,column=1)
n_row += 1
tk.Label(frm, text="版本名称").grid(row=n_row,column=0)
version = tk.Entry(frm, textvariable=file_name,width='40').grid(row=n_row,column=1)
n_row += 1
n_row += 1

n_row += 1
tk.Checkbutton(frm, text = "重启", variable=r_value).grid(row=n_row,column=0)
tk.Checkbutton(frm, text = "保存配置", variable=save_config).grid(row=n_row,column=1)
tk.Checkbutton(frm, text = "升级版本", variable=upload).grid(row=n_row,column=2)
n_row += 1
tk.Label(frm, text="下载进度").grid(row=n_row,column=0)
progressbarOne = tk.ttk.Progressbar(frm, length=280, mode='determinate', maximum=100)
progressbarOne.grid(row=n_row,column=1)
satus = tk.Label(frm, textvariable=status_text, fg='blue').grid(row=n_row,column=2)
n_row += 1
n_row += 1
submit=tk.Button(frm, text="设备升级", command=upload_thread).grid(row=n_row,column=1)
submit=tk.Button(frm, text="取消升级", command=cancel_thread).grid(row=n_row,column=2)
error = tk.Label(frmlog, textvariable=error_text, fg='red').grid(row=n_row,column=0)
# 滚动文本框
#scrolW = 100 # 设置文本框的长度
#scrolH = 25 # 设置文本框的高度
#log = scrolledtext.ScrolledText(frmlog, width=scrolW, height=scrolH, wrap=tk.WORD)
#log.grid(row=0,column=0)

root.mainloop()